package com.sky.pay.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.sky.pay.api.request.WeixinPayOrderRequest;
import com.sky.pay.common.WeixinNotify;
import com.sky.pay.common.WeixinRouter;
import com.sky.pay.config.WeixinPayConfig;
import com.sky.pay.service.OrderService;
import com.sky.pay.service.WeixinJsapiPayService;
import com.sky.pay.vo.Order;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.PrivateKey;
import java.security.Signature;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * @author yangsk
 */
@Service
@Slf4j
public class WeixinJsapiPayServiceImpl implements WeixinJsapiPayService {
    @Autowired
    private OrderService orderService;
    @Autowired
    private WeixinPayConfig weixinPayConfig;

    public String jsapiPay(WeixinPayOrderRequest request) throws IOException {
        Order order = orderService.createOrder(request);
        log.info("商户调用微信的统一下单接口");
        String payUrl = weixinPayConfig.getDomain().concat(WeixinRouter.WEIXIN_CRATE_ORDER_URL.toString());

        //组装数据向微信发起付款交易
        HttpPost httpPost = new HttpPost(payUrl);
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type","application/json; charset=utf-8");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectMapper objectMapper = new ObjectMapper();
        //组装统一下单请求数据
        ObjectNode rootNode = createObjectNode(request,order,objectMapper);

        String bodyAsString = "";//交易结果
        CloseableHttpResponse response = null;
        String ciphertext="";
        try{
            log.info("商家组装好微信交易付款单的请求数据后向微信发起支付请求的参数对象:{}",rootNode);
            objectMapper.writeValue(bos, rootNode);
            httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
            response = weixinPayConfig.getWinxinClient().execute(httpPost);
            bodyAsString = EntityUtils.toString(response.getEntity());
            System.out.println(bodyAsString);
            //将结果转成json
            JSONObject parseJson = (JSONObject) JSON.parse(bodyAsString);
            Integer status = parseJson.getInteger("status");
            if(200 == status){
                log.info("商户向微信发起的下单支付交易成功,对数据进行签名后返回");
                ciphertext = this.encryptData(parseJson);
            }

        }catch (Exception e){
            log.info("商家组装好微信交易付款单的请求数据后向微信发起支付请求,交易失败的参数对象:{}",rootNode);
            e.printStackTrace();
        }finally {
            response.close();
        }
        //注意：这里只返回了密文。在实际的业务中应当根据实际情况，根据开发文档返回密文和对应的字段数据
        return ciphertext;

    }

    private ObjectNode createObjectNode(WeixinPayOrderRequest request,Order order,ObjectMapper objectMapper){
        //微信的回调地址
        String paySuccessNotifyUrl = weixinPayConfig.getNotifyDomain().concat(WeixinNotify.WEIXIN_ORDER_PAY_SUCCESS.toString());
        //交易金额
        Map amount = new HashMap<String,Object>();
        amount.put("totle",order.getTotleFee().intValue());
        amount.put("currency","CNY");

        ObjectNode rootNode = objectMapper.createObjectNode();
        rootNode.put("mchid",weixinPayConfig.getMerchantId())
                .put("appid", weixinPayConfig.getAppid())
                .put("description", order.getTitle())
                .put("notify_url", paySuccessNotifyUrl)
                .put("out_trade_no", order.getOrderNo());
        rootNode.putObject(amount.toString())
                .put("total", request.getProductTotal());
        rootNode.putObject("payer")
                .put("openid", request.getWeixinOpenId());
        return rootNode;
    }

    /**针小程序除外的支付
     * jsapi下单成功,组装前端页面所需要的数据并对数据进行签名将其返回给前端页面
     * 参与签名字段及格式：
     * 应用ID
     * 时间戳
     * 随机字符串
     * 订单详情扩展字符串
     */
    private String encryptData(JSONObject parseJson) throws Exception {
        String appId = weixinPayConfig.getAppid();
        long timeStamp = System.currentTimeMillis()/1000; //秒
        String nonceStr = UUID.randomUUID().toString().replace("-","").substring(0,32);
        String packag =  (String)parseJson.get("prepar_id");
        StringBuffer sb = new StringBuffer();
        sb.append(appId).append("\n");
        sb.append(timeStamp).append("\n");
        sb.append(nonceStr).append("\n");
        sb.append(packag).append("\n");
        //针对小程序除外发起支付的支付
        //return RsaCryptoUtil.encryptOAEP(sb.toString(),verifier.getValidCertificate());
        //针对小程序支付发起的支付
        return sign(parseJson);
    }
    /**针对小程序支付的签名
     * 下单成功,组装前端页面所需要的数据并对数据进行签名将其返回给前端页面
     * 参与签名字段及格式：
     * 应用ID
     * 时间戳
     * 随机字符串
     * 订单详情扩展字符串
     */
    String sign(JSONObject parseJson) throws Exception {
        String appId = weixinPayConfig.getAppid();
        long timeStamp = System.currentTimeMillis()/1000; //秒
        String nonceStr = UUID.randomUUID().toString().replace("-","").substring(0,32);
        String packag =  (String)parseJson.get("prepar_id");
        StringBuffer sb = new StringBuffer();
        sb.append(appId).append("\n");
        sb.append(timeStamp).append("\n");
        sb.append(nonceStr).append("\n");
        sb.append(packag).append("\n");

        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(getMchantPriviteKey());
        sign.update(sb.toString().getBytes("utf-8"));
        return Base64.getEncoder().encodeToString(sign.sign());
    }

    /**
     * 获取商户私钥
     * @return
     */
    private PrivateKey getMchantPriviteKey(){
        PrivateKey merchantPrivateKey = null;
        try{
            merchantPrivateKey = PemUtil.loadPrivateKey(
                    new FileInputStream(weixinPayConfig.getPrivateKeypath()));
        }catch (Exception e){
            new RuntimeException("获取根目录下的商户文件的商户私钥失败",e);
        }
        return merchantPrivateKey;
    }
}
